﻿using PropertyChanged;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using VIA.Integration.Domain.Production;
using VIA.SiMoJi.Domain.Inspection;

namespace VIA.Project.Domain.Services
{
    public interface IProductionService
    {
        event EventHandler<ProductionOutputEventArgs> Output;
        event EventHandler<AlgorithhmExceptionEventArgs> AlgorithmException;
    }
    public interface IProductionPerformance
    {
        float PPM { get; }
        float UPH { get; }
        int OKCount { get; }
        int NgCount { get; }
        int Total { get; }
        float Yield { get; }
        public TimeSpan LastCT { get; }
        public TimeSpan MaxCT { get; }
        public TimeSpan MinCT { get; }
        public TimeSpan AvgCT { get; }
        void Reset();
    }
    public class ProductionOutputEventArgs : EventArgs
    {
        public IProduct Product { get; }
        public ProductionOutputEventArgs(IProduct product)
        {
            Product = product;
        }
    }


    [AddINotifyPropertyChangedInterface]
    public class ProductionService : IProductionService, IProductionPerformance
    {
        #region Fields
        Inspector _inspector;
        #endregion

        #region Property
        PerformanceCaculator _ppmCaculator;

        [DependsOn(nameof(OKCount), nameof(NgCount))]
        public int Total => OKCount + NgCount;

        public float PPM { get; private set; }
        [DependsOn(nameof(PPM))]
        public float UPH => PPM * 60;
        public int OKCount { get; private set; }
        public int NgCount { get; private set; }
        public TimeSpan LastCT { get; private set; }
        public TimeSpan MaxCT { get; private set; }
        public TimeSpan MinCT { get; private set; }
        public TimeSpan AvgCT { get; private set; }
        [DependsOn(nameof(OKCount), nameof(NgCount))]
        public float Yield
        {
            get
            {
                if (Total == 0)
                    return 0;
                else
                    return OKCount / Total;
            }
        }
        #endregion

        #region Events

        public event EventHandler<ProductionOutputEventArgs> Output;
        public event EventHandler<AlgorithhmExceptionEventArgs> AlgorithmException;

        #endregion

        #region Constructor
        public ProductionService(Inspector inspector)
        {
            _ppmCaculator = new PerformanceCaculator(1000, 60_000, x => this.PPM = x.Frequency * 60);
            _ppmCaculator.Start();
            _inspector = inspector;
        }
        #endregion

        #region Methods


        object _locker = new object();
        Queue<TimeSpan> _recentCTList = new Queue<TimeSpan>();
        public void Add(IProduct product)
        {
            lock (_locker)
            {
                var ct = product.CompletedTime.Value - product.CreateTime;
                if (product.Quality == Integration.Domain.Quality.Ok)
                {
                    //_inspector.Scratchfile.OKCount = ++OKCount;

                }
                else
                {
                    //_inspector.Scratchfile.NGCount = ++NgCount;
                }
                LastCT = ct;
                MaxCT = ct > MaxCT ? ct : MaxCT;
                MinCT = MinCT == default ? ct : ct < MinCT ? ct : MinCT;
                _recentCTList.Enqueue(ct);
                if (_recentCTList.Count > 100)
                    _recentCTList.Dequeue();
                //AvgCT = TimeSpan.FromTicks(Convert.ToInt64(_recentCTList.Average()));
                AvgCT = _recentCTList.Average();
                _ppmCaculator.Tick();
            }
            Output?.SafeInvoke(this, new ProductionOutputEventArgs(product));
        }

        public void Reset()
        {
            NgCount = 0;
            OKCount = 0;
            LastCT = default;
            MinCT = default;
            AvgCT = default;
            MaxCT = default;
            _recentCTList.Clear();
            _ppmCaculator.Restart();
        }

        #endregion

    }
}
